package com.tengfei.main.exam.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.tengfei.common.core.domain.PageQuery;
import com.tengfei.common.core.page.TableDataInfo;
import com.tengfei.common.exception.ServiceException;
import com.tengfei.common.helper.DataBaseHelper;
import com.tengfei.common.utils.StringUtils;
import com.tengfei.main.exam.domain.Paper;
import com.tengfei.main.exam.domain.PaperQu;
import com.tengfei.main.exam.domain.PaperUser;
import com.tengfei.main.exam.domain.PaperUserQu;
import com.tengfei.main.exam.domain.bo.PaperUserQuBo;
import com.tengfei.main.exam.domain.bo.UserRecodeBo;
import com.tengfei.main.exam.domain.vo.PaperVo;
import com.tengfei.main.exam.domain.vo.UserRecordObjVo;
import com.tengfei.main.exam.domain.vo.UserRecordSubVo;
import com.tengfei.main.exam.domain.vo.UserRecordVo;
import com.tengfei.main.exam.mapper.PaperMapper;
import com.tengfei.main.exam.mapper.PaperQuMapper;
import com.tengfei.main.exam.mapper.PaperUserMapper;
import com.tengfei.main.exam.mapper.PaperUserQuMapper;
import com.tengfei.main.exam.service.IRecodeService;
import com.tengfei.main.question.domain.vo.ObjectiveVo;
import com.tengfei.main.question.domain.vo.SubjectiveVo;
import com.tengfei.main.question.mapper.ObjectiveMapper;
import com.tengfei.main.question.mapper.SubjectiveMapper;
import com.tengfei.main.system.domain.SysDept;
import com.tengfei.main.system.domain.SysUser;
import com.tengfei.main.system.mapper.SysDeptMapper;
import com.tengfei.main.system.mapper.SysUserMapper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author feige
 * @version v1.0
 * @date 2022-09-12-17:08
 * @description
 */

@Service
public class RecodeServiceImpl implements IRecodeService {

    @Resource
    private PaperUserMapper paperUserMapper;
    @Resource
    private PaperMapper paperMapper;
    @Resource
    private SysDeptMapper deptMapper;
    @Resource
    private PaperUserQuMapper userQuMapper;
    @Resource
    private ObjectiveMapper objectiveMapper;
    @Resource
    private SubjectiveMapper subjectiveMapper;
    @Resource
    private PaperQuMapper paperQuMapper;
    @Resource
    private SysUserMapper userMapper;

    /**
     * 查询试卷列表
     *
     * @param paperName
     * @param num
     * @return
     */
    @Override
    public List<PaperVo> queryPaperList(String paperName, Integer num) {
        if (num <= 0) {
            num = 10;
        }
        return paperMapper.selectVoList(new LambdaQueryWrapper<Paper>()
                .like(ObjectUtil.isNotNull(paperName), Paper::getPaperName, paperName)
                .orderByDesc(Paper::getUpdateTime)
                .last("limit " + num));
    }

    /**
     * 查询某门考试下用户的考试成绩
     *
     * @param bo
     * @param pageQuery
     * @return
     */
    @Override
    public TableDataInfo<UserRecordVo> queryUserRecordPage(UserRecodeBo bo, PageQuery pageQuery) {
        Page<UserRecordVo> userRecodeVoPage = paperUserMapper.selectUserRecode(pageQuery.build(), buildQueryWrapperUser(bo), bo);
        return TableDataInfo.build(userRecodeVoPage);
    }

    /**
     * 导出用户考试成绩信息
     *
     * @param bo
     * @return
     */
    @Override
    public List<UserRecordVo> exportUserRecordList(UserRecodeBo bo) {
        List<UserRecordVo> userRecodeList = paperUserMapper.exportUserRecode(buildQueryWrapperUser(bo), bo);
        // 查询试卷总分
        Paper paper = paperMapper.selectById(bo.getPaperId());
        if (ObjectUtil.isNotNull(paper)) {
            userRecodeList.forEach(item -> {
                item.setPaperScore(paper.getPaperNowScore());
            });
        }
        return userRecodeList;
    }

    /**
     * 查询用户试卷题目详细信息
     *
     * @param userId
     * @param paperId
     * @return
     */
    @Override
    public List userAnsList(Long userId, Long paperId) {
        List<PaperUserQu> paperUserQus = userQuMapper.selectList(new LambdaQueryWrapper<PaperUserQu>()
                .eq(PaperUserQu::getPaperId, paperId)
                .eq(PaperUserQu::getUserId, userId));

        List ansList = new ArrayList(paperUserQus.size());
        paperUserQus.forEach(item -> {
            if (isObj(item.getQuType())) {
                ObjectiveVo objectiveVo = objectiveMapper.selectVoById(item.getQuId());
                UserRecordObjVo userObjVo = BeanUtil.copyProperties(objectiveVo, UserRecordObjVo.class);
                PaperQu paperQu = paperQuMapper.selectOne(new LambdaQueryWrapper<PaperQu>()
                        .eq(PaperQu::getPaperId, paperId)
                        .eq(PaperQu::getQuId, item.getQuId()));
                userObjVo.setQuScore(ObjectUtil.isNotNull(paperQu) ? paperQu.getQuScore() : 0);
                userObjVo.setUserAnswer(item.getUserAnswer());
                userObjVo.setUserScore(item.getUserScore());
                userObjVo.setId(item.getId());
                userObjVo.setUserId(userId);
                ansList.add(userObjVo);
            } else {
                SubjectiveVo subjectiveVo = subjectiveMapper.selectVoById(item.getQuId());
                UserRecordSubVo userSubVo = BeanUtil.copyProperties(subjectiveVo, UserRecordSubVo.class);
                PaperQu paperQu = paperQuMapper.selectOne(new LambdaQueryWrapper<PaperQu>()
                        .eq(PaperQu::getPaperId, paperId)
                        .eq(PaperQu::getQuId, item.getQuId()));
                userSubVo.setQuScore(ObjectUtil.isNotNull(paperQu) ? paperQu.getQuScore() : 0);
                userSubVo.setUserAnswer(item.getUserAnswer());
                userSubVo.setUserScore(item.getUserScore());
                userSubVo.setId(item.getId());
                userSubVo.setUserId(userId);
                ansList.add(userSubVo);
            }
        });
        return ansList;
    }

    /**
     * 更改题目分数
     *
     * @param bo
     * @return
     */
    @Transactional
    @Override
    public boolean updateUserScore(PaperUserQuBo bo) {
        // 更改用户总分
        boolean res = paperUserMapper.update(null, new LambdaUpdateWrapper<PaperUser>()
                .setSql("`user_paper_score`=`user_paper_score`+" + bo.getUserScore())
                .eq(PaperUser::getPaperId, bo.getPaperId())
                .eq(PaperUser::getUserId, bo.getUserId())) > 0;
        // 更改题目分数
        userQuMapper.update(null, new LambdaUpdateWrapper<PaperUserQu>()
                .set(PaperUserQu::getUserScore, bo.getUserScore())
                .eq(PaperUserQu::getId, bo.getId()));
        return res;
    }

    /**
     * 查询用户排行榜
     *
     * @param paperId
     * @return
     */
    @Override
    public List<Map<String, Object>> userRanking(Long paperId) {
        List<PaperUser> paperUsers = paperUserMapper.selectList(new LambdaQueryWrapper<PaperUser>()
                .eq(PaperUser::getPaperId, paperId)
                .orderByDesc(PaperUser::getUserPaperScore));
        List<Map<String, Object>> list = new ArrayList<>(paperUsers.size());
        int index = 0;
        for (PaperUser item : paperUsers) {
            SysUser sysUser = userMapper.selectById(item.getUserId());
            if (ObjectUtil.isNotNull(sysUser)) {
                index++;
                Map<String, Object> map = new HashMap<>();
                String name = sysUser.getName();
                map.put("name", name);
                map.put("score", item.getUserPaperScore());
                map.put("rank", index);
                list.add(map);
            }
        }
        return list;
    }

    /**
     * 查询试卷统计信息
     *
     * @param paperId
     * @return
     */
    @Override
    public Map<String, Object> paperCensus(Long paperId) {
        Map<String, Object> map = new HashMap<>();
        // 扇形统计图
        Paper paper = paperMapper.selectById(paperId);
        List<PaperUser> paperUsers = paperUserMapper.selectList(new LambdaQueryWrapper<PaperUser>()
                .eq(PaperUser::getPaperId, paperId)
                .orderByAsc(PaperUser::getUserPaperScore));
        // 合格人数
        long count1 = paperUsers.stream()
                .filter(item -> ObjectUtil.isNotNull(item.getUserPaperScore()) &&
                        item.getUserPaperScore() >= paper.getPaperPassScore())
                .count();
        // 不合格人数
        int count2 = (int) (paperUsers.size() - count1);
        Map<String, Object> sectorChart = new HashMap<>();
        sectorChart.put("adopt", count1);
        sectorChart.put("fail", count2);
        map.put("sector", sectorChart);
        // 折线统计图
        List<Integer> scoreList = paperUsers.stream().map(item -> item.getUserPaperScore()).collect(Collectors.toList());
        // 总分七份后的间隔
        int space = paper.getPaperNowScore() / 7;
        // 余数
        int remainder = paper.getPaperNowScore() % 7;
        int base = space + remainder;
        int num1 = 0, num2 = 0, num3 = 0, num4 = 0, num5 = 0, num6 = 0, num7 = 0;
        for (Integer score : scoreList) {
            if (ObjectUtil.isNull(score)) {
                num1++;
                continue;
            }
            if (0 <= score && score < base) {
                num1++;
                continue;
            } else if (base <= score && score < base + space) {
                num2++;
                continue;
            } else if (base + space <= score && score < base + space * 2) {
                num3++;
                continue;
            } else if (base + space * 2 <= score && score < base + space * 3) {
                num4++;
                continue;
            } else if (base + space * 3 <= score && score < base + space * 4) {
                num5++;
                continue;
            } else if (base + space * 4 <= score && score < base + space * 5) {
                num6++;
                continue;
            } else if (base + space * 5 <= score && score <= base + space * 6) {
                num7++;
            }
        }
        List<String> labels = new ArrayList(7);
        labels.add(0 + "到" + base + "分");
        labels.add(base + "到" + (base + space) + "分");
        labels.add((base + space) + "到" + (base + space * 2) + "分");
        labels.add((base + space * 2) + "到" + (base + space * 3) + "分");
        labels.add((base + space * 3) + "到" + (base + space * 4) + "分");
        labels.add((base + space * 4) + "到" + (base + space * 5) + "分");
        labels.add((base + space * 5) + "到" + (base + space * 6) + "分");
        List<Integer> values = new ArrayList<>(7);
        values.add(num1);
        values.add(num2);
        values.add(num3);
        values.add(num4);
        values.add(num5);
        values.add(num6);
        values.add(num7);
        Map<String, Object> line = new HashMap<>();
        line.put("label", labels);
        line.put("value", values);
        map.put("line", line);
        return map;
    }

    private Wrapper<SysUser> buildQueryWrapperUser(UserRecodeBo studyMapBo) {
        QueryWrapper<SysUser> wrapper = Wrappers.query();
        wrapper
                .like(StringUtils.isNotBlank(studyMapBo.getName()), "u.name", studyMapBo.getName())
                .and(ObjectUtil.isNotNull(studyMapBo.getDeptId()), w -> {
                    List<SysDept> deptList = deptMapper.selectList(new LambdaQueryWrapper<SysDept>()
                            .select(SysDept::getDeptId)
                            .apply(DataBaseHelper.findInSet(studyMapBo.getDeptId(), "ancestors")));
                    List<Long> ids = deptList.stream().map(SysDept::getDeptId).collect(Collectors.toList());
                    ids.add(studyMapBo.getDeptId());
                    w.in("u.dept_id", ids);
                });
        return wrapper;
    }

    // 非空判断+界限判断+是否为客观题
    private boolean isObj(Integer quType) {
        if (quType.intValue() < 1 || quType.intValue() > 5) {
            throw new ServiceException("题目类型不符合规范");
        } else if (quType.intValue() >= 1 && quType.intValue() <= 3) {
            return true;
        } else {
            return false;
        }
    }
}
